
use std::io::{Result,ErrorKind,Error};
use std::fmt;

use super::cpblock::CPBlock;
use super::head::Headed;
use super::addons::{Addons,Addon};

use super::head::AHead;
use super::mbox::MBox;
use super::layout;
use super::gp::{GTP,GP};
use super::raw::Raw;

use super::ubox::UBox;
use super::bmem::{Bar,BarInc};

pub struct AddonMem<L> {
    bar:MBox<Bar>,
    _marker: std::marker::PhantomData<L>
}

impl<L> BarInc for AddonMem<L> {
    fn inc(&mut self,s :usize) -> usize {self.bar.inc(s)}
    fn pos(&self) -> usize {self.bar.pos}
}

impl<L> Headed for AddonMem<L> {
    type ehead = Bar;
}

impl Addons for AddonMem<layout::R> {
    fn init<T:Headed>(cpb: &CPBlock<T>) -> Result<AddonMem<layout::R>> {
        let bar = AHead::<T>::addon::<Bar>(&cpb.head)?; 
        Ok(AddonMem::<layout::R>{bar:bar,_marker:std::marker::PhantomData::default()}) 
    }
}
impl Addons for AddonMem<layout::C> {
    fn init<T:Headed>(cpb: &CPBlock<T>) -> Result<AddonMem<layout::C>> {
        let bar = cpb.head.as_subtype::<Bar>(layout::ADDONPOST)?;
        Ok(AddonMem::<layout::C>{bar:bar,_marker:std::marker::PhantomData::default()}) 
    }
}

impl<T:Addons+BarInc+Headed> Addon<T> {
    pub fn new_gtp<P:Sized>(&mut self) -> Result<GTP<P>> {
        match &self.block.data {
            None => Err(Error::new(ErrorKind::Other,"donot have mem")),
            Some(mr) => {
                let s = std::mem::size_of::<P>();
                let o = self.addon.inc(s);
                Ok(GTP::<P>::from_gp(&GP{id:mr.id,offset:o as u64, size:s as u64})?) 
            }
        }
    }
    pub fn get_mem(&self,off :usize) -> Raw {
        match &self.block.data {
            None => Raw::default(),
            Some(mr) => mr.offset_to_raw(off),
        }
    }
    pub fn new_gpr<P:Sized>(&mut self) -> Result<(GTP<P>,Raw)> {
        let gtp = self.new_gtp::<P>()?;
        let offset = gtp.gp.offset.clone();
        Ok((gtp,self.get_mem(offset as usize)))
    }
    pub fn new<P:Sized>(&mut self) -> Result<UBox<P>> {
        let (gtp,raw) = self.new_gpr::<P>()?;
        Ok(UBox::<P>::from_gpr(gtp,raw)?)
    }
    pub fn new_gp(&mut self,s :usize) -> Result<GP> {
        match &self.block.data {
            None => Err(Error::new(ErrorKind::Other,"donot have mem")),
            Some(mr) => {
                let o = self.addon.inc(s);
                Ok(GP{id:mr.id,offset:o as u64, size:s as u64})
            } 
        }
    }
}
impl<T:BarInc+Headed+Addons> fmt::Display for Addon<T> { 
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{} bar:{}",self.block,self.addon.pos())
    }
}

pub type Mem = Addon<AddonMem<layout::R>>;
pub type CMem = Addon<AddonMem<layout::C>>;


